package org.myspringframework.core;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ClassPathXmlApplicationContext implements ApplicationContext{
    //模拟spring一级缓存，存放所有初始化后的单例对象
    private Map<String,Object> singletonObjects=new HashMap<>();
    private Logger logger =LoggerFactory.getLogger(ClassPathXmlApplicationContext.class);

    /*解析myspring的配置文件，初始化所有的Bean对象
    * configLocation spring配置文件的路径*/
    public ClassPathXmlApplicationContext(String configLocation) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        try {
            //解析spring配置文件，然后实例化Bean，将Bean放入singletonObjects集合当中（曝光）
            SAXReader reader=new SAXReader();
            //获取系统的类加载器，获取一个输入流
            ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
            InputStream in = systemClassLoader.getResourceAsStream(configLocation);
            //读取流，转换为文件
            Document read = reader.read(in);
            //获取bean标签的节点集合
            List <Node> nodes = read.selectNodes("//bean");
            for (Node node : nodes) {
                System.out.println(node);
                //转换为Element，继承于Node，方法更丰富
                Element beanEdit = (Element) node;
                //获取id属性
                String id = beanEdit.attributeValue("id");
                //获取class属性
                String className = beanEdit.attributeValue("class");
                System.out.println("id:" + id);
                System.out.println("class:" + className);
                //反射机制创建对象，放入map集合
                Class<?> clazz = Class.forName(className);
                Constructor<?> defaultCon = clazz.getDeclaredConstructor();
                Object bean = defaultCon.newInstance();
                //将bean曝光放入map
                singletonObjects.put(id,bean);
                System.out.println("singletonObjects:"+singletonObjects.toString());
            }
            nodes.forEach(node -> {
                Element element = (Element) node;
                String id = element.attributeValue("id");

                String className = element.attributeValue("class");

                //获取属性标签的节点集合
                List <Element>properties = element.elements("property");
                properties.forEach(property -> {
                    //获取属性名
                    String propertyName = property.attributeValue("name");
                    //获取set方法名
                    String setName="set"+propertyName.toUpperCase().charAt(0)+propertyName.substring(1);

                    try {
                        //获取属性类型
                        Class<?> type = singletonObjects.get(id).getClass().getDeclaredField(propertyName).getType();
                        Method setMethod = singletonObjects.get(id).getClass().getDeclaredMethod(setName, type);
                        //获取属性值
                        String propertyValue = property.attributeValue("value");
                        String propertyRef = property.attributeValue("ref");
                        if(propertyRef!=null){
                            setMethod.invoke(singletonObjects.get(id),singletonObjects.get(propertyRef));
                        }

                    } catch (NoSuchFieldException e) {
                        throw new RuntimeException(e);
                    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                        throw new RuntimeException(e);
                    }
                });

            });
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }


    }
    @Override
    public Object getBean(String beanName) {
        return null;
    }
}
